#define _CRT_SECURE_NO_WARNINGS    // vs编译器老传统（不加 _s 会报错说使用不安全）
/* 哈夫曼压缩源文件（头文件中定义方法的实现） */

#include "HuffmanCompresion.h"

#define BUF_SIZE 1024   // 1k bytes
#define BYTE_BITS 8
#define HIGEST_BIT 0x80   //检测最高位比特

using std::cout;
using std::endl;


typedef hfCompresion HfmC;  // 类名重命名

// 构造：按ASCII码初始化
HfmC::hfCompresion(){
	_fileAscii.resize(_ASCII);
	for (size_t i = 0; i < _ASCII; ++i)
		_fileAscii[i]._ch = i;
}

void HfmC::HuffmanCode(hfTree_node<bytes>* root){
	// 从叶子到根逆向求每个字符的哈夫曼编码
	if (nullptr == root) return;

	HuffmanCode(root->_left);
	HuffmanCode(root->_right);

	if (nullptr == root->_left && nullptr == root->_right){
		string& Code = _fileAscii[root->_weight._ch]._chCode;
		auto* node = root;
		auto* parent = root->_parent;
		// 编码
		while (parent != nullptr){
			if (node == parent->_left) Code += '0';
			if (node == parent->_right) Code += '1';
			// 更新
			node = parent;
			parent = node->_parent;
		}
		reverse(Code.begin(), Code.end());
	}
}

string HfmC::GetFileName(const string& filepath){
	size_t len = filepath.size() - (filepath.substr(filepath.find_last_of('.'))).size();
	return filepath.substr(0, len);
}

void HfmC::Compresion(const string& filepath){
	// 统计文件每个字符出现次数
	FILE* in = fopen(filepath.c_str(), "rb");
	if (nullptr == in){  cout << "要打开的文件不存在" << endl;	return; }

	uch readbuf[BUF_SIZE];
	while (true){
		size_t readSize = fread(readbuf, 1, BUF_SIZE, in);
		if (NULL == readSize) break;    // 读取完毕

		// 对应字符出现次数加 1
		for (size_t i = 0; i < readSize; ++i)
			++_fileAscii[readbuf[i]]._occurCnt;
	}

	bytes invalid;   // 默认构造的无效值（出现次数0次）
	// 创建读取字符结果的哈夫曼树
	HuffmanTree<bytes> hft(_fileAscii, invalid);
	
	// 获取哈夫曼编码
	HuffmanCode(hft.getRoot());

	
	// 对文件进行编码压缩
	string file_name = GetFileName(filepath);
	file_name += ".hfzip";
	FILE* fcompresion = fopen(file_name.c_str(), "wb");
	// 填充文件属性
	AddFileAttributes(filepath, fcompresion);

	fseek(in, 0, SEEK_SET);
	BitSet Bits;
	while (true){
		size_t readSize = fread(readbuf, 1, BUF_SIZE, in);
		if (NULL == readSize) break;

		for (size_t i = 0; i < readSize; ++i){
			string& Code = _fileAscii[readbuf[i]]._chCode;
			for (size_t j = 0; j < Code.size(); ++j){
				Bits._bit <<= 1;
				if ('1' == Code[j]) Bits._bit |= 1;
				Bits._bitCnt += 1;
				if (BYTE_BITS == Bits._bitCnt) {
					fputc(Bits._bit, fcompresion);
					Bits._bitCnt = NULL;
				}
			}
		}
	}
	// 填充剩余比特位
	if (Bits._bitCnt > 0){
		Bits._bit <<= (BYTE_BITS - Bits._bitCnt);
		fputc(Bits._bit, fcompresion);
	}
	
	fclose(in);
	fclose(fcompresion);
	cout << "压缩成功" << endl;
}

void HfmC::GetLine(FILE* in, string& str){
	while (!feof(in)){
		uch ch = fgetc(in);
		if (ch == '\n') break;
		str += ch;
	}
	
}

string HfmC::GetFilePostfix(const string& filepath){
	return filepath.substr(filepath.find_last_of('.') + 1);
}

void HfmC::AddFileAttributes(const string& filePath, FILE* fcompresion){
	string fileAttr;
	// 获取文件后缀
	fileAttr += GetFilePostfix(filePath);
	fileAttr += '\n';
	// 出现频次
	size_t Line = 0;
	string Info;
	for (auto i : _fileAscii){
		if (NULL == i._occurCnt) continue;

		Info += i._ch;
		Info += ':';
		Info += std::to_string(i._occurCnt);
		Info += '\n';
		++Line;
	}
	fileAttr += std::to_string(Line);
	fileAttr += '\n';
	fwrite(fileAttr.c_str(), 1, fileAttr.size(), fcompresion);
	fwrite(Info.c_str(), 1, Info.size(), fcompresion);
}

// 利用哈夫曼树解压
void HfmC::Uncompresion(const string& filepath){
	// 从文件属性读取信息恢复哈夫曼树
	FILE* in = fopen(filepath.c_str(), "rb");
	if (nullptr == in){ cout << "要解压的文件不存在" << endl;	return; }
	// 获取后缀
	string file_name = "new" + GetFileName(filepath) + ".";
	string UnComFile(file_name.c_str());
	string str;
	GetLine(in, str);

	UnComFile += str;
	// 获取行数与频次
	str = "";
	GetLine(in, str);
	size_t Line = atoi(str.c_str());
	for (size_t i = 0; i < Line; ++i){
		str = "";
		GetLine(in, str);
		if ("" == str){  // 说明读取的是换行'\n'
			str += '\n';
			GetLine(in, str);
		}
		_fileAscii[(uch)str[0]]._occurCnt = atoi(str.c_str() + 2);
	}

	// 恢复哈夫曼树
	bytes invalid;
	HuffmanTree<bytes> hft(_fileAscii, invalid);

	// 解压
	FILE* uncompress = fopen(UnComFile.c_str(), "wb");
	uch readbuf[BUF_SIZE];

	hfTree_node<bytes>* node = hft.getRoot();
	size_t FileSize = 0;
	while (true){
		size_t rdSize = fread(readbuf, 1, BUF_SIZE, in);
		if (NULL == rdSize) break;

		for (size_t i = 0; i < rdSize; ++i){
			uch ch = readbuf[i];
			for (size_t j = 0; j < BYTE_BITS; ++j){
				if (ch & HIGEST_BIT) node = node->_right;
				else node = node->_left;
				ch <<= 1;
				if (node->_left == nullptr && node->_right == nullptr){
					fputc(node->_weight._ch, uncompress);
					node = hft.getRoot();
					FileSize += 1;
					if (FileSize == node->_weight._occurCnt) break;
				}
			}
		}
	}
	fclose(in);
	fclose(uncompress);
	cout << "解压成功" << endl;
}

